//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_TIMEPEG_H__
#define __ELASTOS_TIMEPEG_H__

// TODO: need to be port to ARM and MIPS platform
// "defined(_x86)" needed to be extended, it only can be use on x86 now

#if (_TEST_TYPE == 3)     /* Test for schedule performance */ \
    || (_TEST_TYPE == 21) /* Test for x86 system clock \
                             interruption response performance */ \
    || (_TEST_TYPE == 22) /* Test for x86 disable and \
                             restore preemption performance */ \
    || (_TEST_TYPE == 23) /* Test for x86 disable and \
                             restore interruption performance */

#ifdef _x86
#include <testing/x86/asm.h>
#elif defined _sharp
#include <testing/arm/sharp/asm.h>
#elif defined _techfaith
#include <testing/arm/techfaith/asm.h>
#elif defined _gingko
#include <testing/arm/gingko/asm.h>
#elif defined _c28
#include <testing/arm/c28/asm.h>
#elif defined _neptune
#include <testing/arm/neptune/asm.h>
#endif

extern struct timepeg_slot *g_timepeg_slots;
extern struct timepeg_slot *g_prev_slots;

extern TIMESTAMP * timepeg_do_hit(struct timepeg_slot * const _ntps, \
        TIMESTAMP * const _now_tp, const int _mode);

extern void kprintf_time_pegs();
extern void tp_schedule_start();
extern void tp_schedule_stop();

/*compare two timepegs*/
inline int timepeg_cmp(TIMESTAMP *left, TIMESTAMP *right)
{
    // letf->high != right->high
    if (left->high > right->high)
        return 1;
    if (left->high < right->high)
        return -1;

    // left->high == right->high
    if (left->low > right->low)
        return 1;
    if (left->low < right->low)
        return -1;

    return 0;
}

inline void timepeg_hit(struct timepeg_slot * const _ntps, const int _mode, \
    int bReset = 0)
{
    TIMESTAMP entered_tp;
    TIMESTAMP *prev_tp;

    // Read Time-Stamp Counter
    timepeg_stamp(&entered_tp, bReset);

    // if _ntps->mode_bit==TIMEPEG_MODE_DEAD, then prev_tp=(TIMESTAMP *)0
    prev_tp = timepeg_do_hit(_ntps, &entered_tp, _mode);

    // Stored the time-stamp after operating the timepeg data structure.
    // This is the real start time for the next operation to measure
    // the performance.
    if (prev_tp) {
        timepeg_stamp(prev_tp, bReset);
    }
}

#define TPH_MODE_START  1
#define TPH_MODE_STOP   2
#define TPH_MODE_BOTH   (TPH_MODE_START | TPH_MODE_STOP)

#define TIMEPEG_MODE(_M_name, _M_mode, _M_reset)                    \
    do {                                                            \
        static struct timepeg_slot _x_Timepeg = {                   \
            _M_name,                                                \
        };                                                          \
        (void)timepeg_hit(&_x_Timepeg, _M_mode, _M_reset);          \
    } while (0);

#define TIMEPEG(_M_name, _M_reset)                                  \
    TIMEPEG_MODE(_M_name, TPH_MODE_BOTH, _M_reset)

/*
 * Define and hit a directed timepeg
 */
#define DTIMEPEG(_M_name, _M_directed_pred)                         \
    do {                                                            \
        static struct timepeg_slot _x_Timepeg = {                   \
            _M_name,                                                \
            _M_directed_pred,                                       \
        };                                                          \
        (void)timepeg_hit(&_x_Timepeg, TPH_MODE_BOTH);              \
    } while (0)

#if (_TEST_TYPE == 3) /* Test for schedule performance */

#define TIMEPEG_SCHEDULE_START    \
        tp_schedule_start();

#define TIMEPEG_SCHEDULE_STOP    \
        tp_schedule_stop();

#else

#define TIMEPEG_SCHEDULE_START
#define TIMEPEG_SCHEDULE_STOP

#endif // _TEST_TYPE == 3

#if (_TEST_TYPE == 21) /* Test for x86 system clock
                          interruption response performance */

#define TIMEPEG_INTERRUPT_START \
        TIMEPEG_ASM("InterruptHandler");

#define TIMEPEG_TIMER_INTERRUPT_STOP \
        DTIMEPEG("TimerPITIsr", "InterruptHandler");

#else

#define TIMEPEG_INTERRUPT_START
#define TIMEPEG_TIMER_INTERRUPT_STOP

#endif // _TEST_TYPE == 21

#if (_TEST_TYPE == 22) /* Test for x86 disable and
                          restore preemption performance */

#define TIMEPEG_DISABLE_PREEMPTION \
        if (bOriginalPreemption) {\
            TIMEPEG_MODE("DisablePreemption", TPH_MODE_START, 1);\
        }

#define TIMEPEG_RESTORE_PREEMPTION \
        if (bPreemption) {\
            DTIMEPEG("RestorePreemption", "DisablePreemption");\
        }

#else

#define TIMEPEG_DISABLE_PREEMPTION
#define TIMEPEG_RESTORE_PREEMPTION

#endif // _TEST_TYPE == 22

#if (_TEST_TYPE == 23) /* Test for x86 disable and
                          restore interruption performance */

#define TIMEPEG_DISABLE_INTERRUPT \
        if (uFlags & EFLAG_IF) {\
            TIMEPEG_MODE("DisableInterrupt", TPH_MODE_START, 1);\
        }

#define TIMEPEG_RESTORE_INTERRUPT \
        if (uFlags & EFLAG_IF) {\
            DTIMEPEG("RestoreInterrupt", "DisableInterrupt");\
        }

#else

#define TIMEPEG_DISABLE_INTERRUPT
#define TIMEPEG_RESTORE_INTERRUPT

#endif // _TEST_TYPE == 23

#else // _TEST_TYPE == 3 || ...

#define TIMEPEG(_M_name, _M_reset)                  EMPTY_EXPRESSION
#define TIMEPEG_MODE(_M_name, _M_mode, _M_reset)    EMPTY_EXPRESSION
#define DTIMEPEG(_M_name, _M_directed_pred)     EMPTY_EXPRESSION
#define TIMEPEG_ASM(_M_name)        EMPTY_EXPRESSION
#define TIMEPEG_SCHEDULE_START
#define TIMEPEG_SCHEDULE_STOP
#define kprintf_time_pegs()         EMPTY_EXPRESSION
#define TIMEPEG_INTERRUPT_START
#define TIMEPEG_TIMER_INTERRUPT_STOP
#define TIMEPEG_DISABLE_PREEMPTION
#define TIMEPEG_RESTORE_PREEMPTION
#define TIMEPEG_DISABLE_INTERRUPT
#define TIMEPEG_RESTORE_INTERRUPT

#endif // _TEST_TYPE == 3 || ...

#endif  /* _TIMEPEG_H_ */
